package org.spring.springboot.dao;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spring.springboot.util.SpringContextHolder;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

public class MybatisRedisCache implements Cache {
	private final static Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
	private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

	private RedisTemplate<Serializable, Serializable> _redisTemplate = null; //(RedisTemplate<Serializable, Serializable>) SpringContextHolder.getBean(RedisTemplate.class);

	private String id;
	private JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();

	@SuppressWarnings("unchecked")
	private RedisTemplate<Serializable, Serializable> getRedisTemplate() {
		if (_redisTemplate == null)
			_redisTemplate = (RedisTemplate<Serializable, Serializable>) SpringContextHolder.getBean(RedisTemplate.class);
		return _redisTemplate;

	}

	public MybatisRedisCache(final String id) {
		logger.info("MybatisRedisCache:"+id);
		if (id == null) {
			throw new IllegalArgumentException("Cache instances require an ID");
		}

		this.id = id;
	}

	@Override
	public String getId() {
		logger.info("getId:"+this.id);
		return this.id;
	}

	@Override
	public void putObject(Object key, Object value) {
		if (value != null) {
			logger.info("putObject id:"+id+" key:"+key.toString());
			getRedisTemplate().opsForValue().set(key.toString(), jdkSerializer.serialize(value), 2, TimeUnit.DAYS);
		}
	}

	@Override
	public Object getObject(Object key) {
		try {
			if (key != null) {
				logger.info("getObject id:"+id+" key:"+key.toString());
				Object obj = getRedisTemplate().opsForValue().get(key.toString());
				return jdkSerializer.deserialize((byte[]) obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Object removeObject(Object key) {
		try {
			if (key != null) {
				logger.info("removeObject id:"+id+" key:"+key.toString());
				getRedisTemplate().expire(key.toString(), 1, TimeUnit.SECONDS);
			}
		} catch (Exception e) {
		}
		return null;
	}

	@Override
	public void clear() {
		// jedis nonsupport
	}

	@Override
	public int getSize() {

		Long size = getRedisTemplate().execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.dbSize();
			}
		});
		logger.info("getSize:"+size.toString());
		return size.intValue();
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		return this.readWriteLock;
	}

}
